\documentclass[a4paper]{article}
\usepackage[spanish]{babel}
\usepackage[utf8]{inputenc}
\usepackage{charter}   % tipografia
\usepackage{graphicx}
%\usepackage{makeidx}

%\usepackage{float}
%\usepackage{amsmath, amsthm, amssymb}
%\usepackage{amsfonts}
%\usepackage{sectsty}
%\usepackage{charter}
%\usepackage{wrapfig}
%\usepackage{listings}
%\lstset{language=C}


\input{codesnippet}
\input{page.layout}
% \setcounter{secnumdepth}{2}
\usepackage{underscore}
\usepackage{caratula}
\usepackage{url}
\usepackage{algpseudocode}

% ******************************************************** %
%              TEMPLATE DE INFORME ORGA2 v0.1              %
% ******************************************************** %
% ******************************************************** %
%                                                          %
% ALGUNOS PAQUETES REQUERIDOS (EN UBUNTU):                 %
% ========================================
%                                                          %
% texlive-latex-base                                       %
% texlive-latex-recommended                                %
% texlive-fonts-recommended                                %
% texlive-latex-extra?                                     %
% texlive-lang-spanish (en ubuntu 13.10)                   %
% ******************************************************** %



\begin{document}


\thispagestyle{empty}
\materia{Organización del Computador II}
\submateria{Segundo Cuatrimestre de 2014}
\titulo{Trabajo Práctico III}
\subtitulo{Grupo El Titanic de Homero / Entraña}
\integrante{Fabi\'an Gonz\'alez}{076/01}{utyman@gmail.com}
\integrante{Jos\'e Massigoge}{954/12}{jmas@live.com.ar}
\integrante{Christian L\'opez}{821/05}{christianlt_85@hotmail.com}


\maketitle
\newpage

\thispagestyle{empty}
\vfill
\begin{abstract}
En el siguiente trabajo, se presenta un kernel sencillo montado sobre una arquitectura intel 8086. Tiene como objetivo poner en pr\'actica las facilidades ofrecidas por el microprocesador en cuanto a protecci\'on, manejo de memoria con segmentaci\'on y paginaci\'on e intercambio de tareas. El kernel manejar\'a estos aspectos en el contexto de un juego territorial de tanques sobre un mapa.
\end{abstract}

\thispagestyle{empty}
\vspace{3cm}
\tableofcontents
\newpage


%\normalsize
\newpage

\section{Objetivos generales}

A lo largo del siguiente trabajo, se fueron aplicando, en forma escalada, los conceptos de programaci\'on de sistemas tratados a lo largo de la materia. El objetivo principal es contruir un sistema simple que permita la ejecuci\'on de procesos o tareas en forma concurrente. Para ello se utilizan las facilidades ofrecidas por la arquitectura INTEL 8086.

\section{Desarrollo} 

El entorno de emulaci\'on que se utiliz\'o fue el provisto por BOCHS. Con ello se puede simular, con algunas limitaciones, un microprocesador IBM-PC compatible con algunos perif\'ericos desde el momento mismo del inicio de la m\'aquina. 
Luego de la ejecuci\'on del bootloader provisto por la c\'atedra nos encontramos ejecutando c\'odigo en modo real

\subsection{Modo Real}

La primera parte del c\'odigo que utiliza nuestro kernel simple se encuentra en el archivo kernel.asm. En esta parte se debe trabajar con 16 bits.  El kernel se encarga de realizar las siguientes tareas:

\begin{itemize}
	\item Imprimir un mensaje de bienvenida provisto por la c\'atedra
	\item Habilitar la l\'inea A20 del procesador.
	\item Cargar la GDT con los segmentos de c\'odigo y de datos solicitados por el enunciado.
	\item Pasar a modo protegido
\end{itemize}	

\subsection{Seteo de Video y Mensaje de Bienvenida}

Se determina un modo de video de 80 x 50. En este espacio es en el que vamos a dibujar el campo y representar los p\'aginas alcanzadas por las diferentes tareas o tanques.
\\
\begin{verbatim}
    mov ax, 0003h
    int 10h ; set mode 03h
    xor bx, bx
    mov ax, 1112h
    int 10h ; load 8x8 font
\end{verbatim}

Posteriormente, se usa una funci\'on provista por la c\'atedra para imprimir en pantalla en modo real un mensaje de bienvenida. Se establece un puntero a la base de la memoria de video y se copia el string proporcionado.\\

\subsection{L\'inea A20}

Se prosigue habilitando la l\'inea A20. Para ello se cuenta con la funci\'on habilitar_a20.

\subsection{Segmentaci\'on - Carga de GDT}

Una vez configurada la GDT, en el archivo kernel.asm simplemente usamos la funci\'on lgdt para cargarla:

\begin{verbatim}
    lgdt [GDT_DESC]    
\end{verbatim}

N\'otese que tenemos  GDT_DESC como una variable externa que definimos en C.

\subsection{Configuraci\'on de la gdt}

Para valernos de las facilidades de segmentaci\'on de la arquitectura, se necesita configurar la gdt. Se requiere hacer esto antes de pasar a modo protegido.
La c\'atedra ofrece una serie de estructuras que nos facilitan la configuraci\'on de la gdt. Se encuentran ubicadas en los archivos gdt.h y gdt.c Nuestra funci\'on fue simplemente agregar los descriptores de segmento solicitados por el enunciado y colocar los valores pertinentes.

As\'i definimos los siguientes segmentos:

\begin{itemize}
	\item el primer segmento nulo como es necesario hacer siempre.
	\item ocho segmentos inv\'alidos de acuerdo a lo indicado por el enunciado.
	\item en el \'indice 9 un segmento de c\'odigo para el kernel, con ese nivel de protecci\'on.
	
\begin{verbatim}
    [GDT_IDX_CODE_0] = (gdt_entry) {
        (unsigned short)    0xDD00,         /* limit[0:15]  */
        (unsigned short)    0x0000,         /* base[0:15]   */
        (unsigned char)     0x00,           /* base[23:16]  */
        (unsigned char)     0x0A,           /* type  R/E    */
        (unsigned char)     0x01,           /* s            */
        (unsigned char)     0x00,           /* dpl          */	
        (unsigned char)     0x01,           /* p            */
        (unsigned char)     0x02,           /* limit[16:19] */
        (unsigned char)     0x00,           /* avl          */
        (unsigned char)     0x00,           /* l            */
        (unsigned char)     0x01,           /* db           */
        (unsigned char)     0x01,           /* g            */
        (unsigned char)     0x00,           /* base[31:24]  */
    },

\end{verbatim}
N\'otese que estamos usando 733MB, por ello el l\'imite se define como 0x2DD00 con el bit de granularidad seteado (es decir que multiplicamos el m\'aximo offset posible por 4K). El dpl es 0 de acuerdo a lo que necesitamos, ya que se trata de un segmento de c\'odigo a usar por el privilegio m\'as alto (es decir 0).
\item el descriptor 10 es el segmento de c\'odigo de nivel de privilegio 0.
\item el descriptor 11 es el segmento de datos de nivel de privilegio 3 (usuario).
\item el descriptor 12 es el segmenteo de c\'odigo de nivel de privilegio 3 (usuario).

\end{itemize}

\subsection{Pasaje a Modo Protegido}

Una vez configurados los descriptores de segmento en la GDT y cargada su posici\'on de memoria en el registro gdtr, en kernel.asm se activa el modo protegido. Para eso nos valemos de las siguientes l\'ineas de c\'odigo:

\begin{verbatim}
    ; Setear el bit PE del registro CR0
    mov eax, cr0
    or eax, 1
    mov cr0, eax
\end{verbatim}

Para hacer uso del modo protegido, debemos hacer un salto a distancia, teniendo en cuenta que el segmento al que debemos ir es el del \'indice 9 (que multiplicado por 8, que es el tamaño de segmento de la gdt nos da 0x48, es decir 72):

\begin{verbatim}
    ; Saltar a modo protegido
    jmp 0x48:mp	; 0x48 = 1001000b -> 1001: descriptor 9 de la GDT;
		;                -> 000: atributos.
\end{verbatim}

\subsection{Configuraci\'on de los registros de segmento y el stack del kernel}

Una vez en modo protegido configuramos la pila y los registros de segmento:

\begin{verbatim}
    ; Configuramos el registro de segmento de datos.
    ; El 0x58 corresponde al descriptor 11 de la GDT, que es un segmento de
    ; datos de nivel (privilegio) cero. Configuramos el registro DS con el
    ; valor de ese segmento.

    mov ax, 0x58
    mov ds, ax  

    ; A los rgistros ES y FS les asignamos el mismo valor del registro DS.
    
    mov es, ax
    mov fs, ax
\end{verbatim}

Como solicita el enunciado, la pila debe tener como base la direcci\'on de memoria 0x00027000.

\begin{verbatim}
    ; Establecer la base de la pila
    mov ss,  ax  ; AX: 0x58
    mov esp, 0x00027000
    mov ebp, esp
\end{verbatim}

\subsection{Paginaci\'on}

El siguiente paso en la inicializaci\'on del sistema es la configuraci\'on de las estructuras de paginaci\'on y el paso a un manejo de memoria que disponga de este recurso del microprocesador. Para ello, el kernel realiza la siguiente operatoria:

\begin{itemize}
	\item creaci\'on de la estructura de directorios y de las tablas de p\'agina.
	\item la configuraci\'on del registro cr3 para que apunte al descriptor correcto.
	\item la activaci\'on de paginaci\'on a trav\'es de la manipulaci\'on de los flags del registro cr0.
\end{itemize}

A continuaci\'on, se describe cada uno de los pasos:

\subsection{Estructura de las tablas de paginaci\'on}

El directorio de p\'aginas para el kernel de acuerdo al enunciado se encuentra en la direcci\'on 0x27000.
La inicializaci\'on del directorio de p\'aginas para el kernel se encuentra en la funci\'on mmu_inicializar_dir_kernel que se encuentra en el archivo mmu.c y definido en el archivo mmu.h.
Dado que se debe hacer identity mapping entre las direcciones 0x00000000 a 0x00DC3FFF, se necesitan cuatro p\'aginas de 4KB. Por lo tanto vamos a necesitar cuatro descriptores de tabla:

\begin{verbatim}
  *((unsigned int*) &pPD[0]) = (PT_KERNEL_0 ) + 0x00000003;
  *((unsigned int*) &pPD[1]) = (PT_KERNEL_1 ) + 0x00000003;
  *((unsigned int*) &pPD[2]) = (PT_KERNEL_2 ) + 0x00000003;
  *((unsigned int*) &pPD[3]) = (PT_KERNEL_3 ) + 0x00000003;

  //
  // Configuramos el resto de las 1024 entradas del PD en valores nulos.
  for(i = 4; i < 1024; i++) {
    *((unsigned int*) &pPD[i]) = 0;
  }

\end{verbatim}

N\'otese que se le est\'a sumando el valor 0x00000003 para activar el bit que indica que la p\'agina se encuentra presente.

Finalmente lo que se configuran cada una de las entradas de la tabla de p\'aginas apuntadas por el directorio de tabla para tener disponibles por identity mapping todas las p\'aginas que necesitamos:

\begin{verbatim}

  // se hace identity mapping de la direccion 0 a la 4194303
  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &pTKernel0[i]) = PAGE_SIZE*i + 0x00000003;
  }

  // se hace identity mapping de la direccion 4194304 a la 8388607
  for (i = 0; i < 1024; i++) {
     *((unsigned int*) &pTKernel1[i]) = PAGE_SIZE*(i + 1024) + 0x00000003;
  }

  // se hace identity mapping de la direccion 8388608 a la 12582912
  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &pTKernel2[i]) = PAGE_SIZE*(i + 2048) + 0x00000003;
  }

  // se hace identity mapping de la direccion 12582913 a la 14434302
  for (i = 0; i < 452; i++) {
    *((unsigned int*) &pTKernel3[i]) = PAGE_SIZE*(i + 3072) + 0x00000003;
  }
\end{verbatim}

Para la inicializaci\'on de la paginaci\'on de tareas definimos una funci\'on similar inicializar_mmu_dir_tarea, que adem\'as se ocupa de pasar el c\'odigo de la tarea al \'area del mapa.

Por otra parte, definimos funciones para mapear una p\'agina virtual a una p\'agina f\'isica pasando como par\'ametro el cr3. Teniendo el directorio de p\'agina lo que vemos es si la entrada en la tabla de p\'aginas est\'a presente y en caso de que no est\'e la mapeamos a la direcci\'on virtual correspondiente:

\begin{verbatim}
// Inicializa (crea) todas las estructuras necesarias para el mecanismo de
// Paginación (pero no lo activa). Interesa hacer 'identity mapping' para
// el rango 0x00000000 a 0x00DC3FFF. Se tienen que crear 4 page tables
// a partir de la direccion 0x28000, 0x29000, 0x2A000 y 0x2B000.
// Por otra parte, el page diretory tiene que empezar en la direccion
// 0x27000.
// Esta función NO activa la
// Paginación; sólo crea las estructuas necesarias.
void mmu_inicializar_dir_kernel() {
  int i = 0;

  unsigned int* pPD = (unsigned int*) PD_DIR;  // Directorio de Páginas.
  unsigned int* pTKernel0 = (unsigned int*) PT_KERNEL_0;  // Tabla de Páginas 0.
  unsigned int* pTKernel1 = (unsigned int*) PT_KERNEL_1;  // Tabla de Páginas 1.
  unsigned int* pTKernel2 = (unsigned int*) PT_KERNEL_2;  // Tabla de Páginas 2.
  unsigned int* pTKernel3 = (unsigned int*) PT_KERNEL_3;  // Tabla de Páginas 3.

  *((unsigned int*) &pPD[0]) = (PT_KERNEL_0 ) + 0x00000003;
  *((unsigned int*) &pPD[1]) = (PT_KERNEL_1 ) + 0x00000003;
  *((unsigned int*) &pPD[2]) = (PT_KERNEL_2 ) + 0x00000003;
  *((unsigned int*) &pPD[3]) = (PT_KERNEL_3 ) + 0x00000003;

  //
  // Configuramos el resto de las 1024 entradas del PD en valores nulos.
  for(i = 4; i < 1024; i++) {
    *((unsigned int*) &pPD[i]) = 0;
  }

  // se hace identity mapping de la direccion 0 a la 4194303
  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &pTKernel0[i]) = PAGE_SIZE*i + 0x00000003;
  }

  // se hace identity mapping de la direccion 4194304 a la 8388607
  for (i = 0; i < 1024; i++) {
     *((unsigned int*) &pTKernel1[i]) = PAGE_SIZE*(i + 1024) + 0x00000003;
  }

  // se hace identity mapping de la direccion 8388608 a la 12582912
  for (i = 0; i < 1024; i++) {
    *((unsigned int*) &pTKernel2[i]) = PAGE_SIZE*(i + 2048) + 0x00000003;
  }

  // se hace identity mapping de la direccion 12582913 a la 14434302
  for (i = 0; i < 452; i++) {
    *((unsigned int*) &pTKernel3[i]) = PAGE_SIZE*(i + 3072) + 0x00000003;
  }

  // el resto pasan a estar no presentes
  for (i = 452; i < 1024; i++) {
    *((unsigned int*) &pTKernel3[i]) = 0x00000000;
  }

}

void mmu_mapear_pagina(unsigned int virtual, unsigned int cr3, unsigned int fisica) {
  unsigned int  numEntradaPD = virtual >> 22;
  unsigned int  numEntradaPT = (virtual >> 12) & 0x3FF;
  unsigned int* pPD = (unsigned int*) cr3;
  unsigned int* pPT = 0x0;

  
  // verificamos si existe la entrada en el directorio de páginas
  if (pPD[numEntradaPD] == 0x00000000) {
    
    // Pedimos un page frame usando malloc_page() . El valor
    // 0x3 que sumamos corresponde a los atributos de la entrada:
    // lectura/escritura y presente (11b = 3).
    pPD[numEntradaPD] = malloc_page() + 0x00000003;
  }

  
  // configuramos la entrada de página
  pPT = (unsigned int*) (pPD[numEntradaPD] & 0xFFFFF000);
  
  
  // La dirección física ya debería estar alineada a 4KB. De cualquier
  // manera, ponemos en cero los 3 nibles menos signicativos y sumamos 0x3
  // para los atributos (lectura/escritura y presente (11b = 3)).
  pPT[numEntradaPT] = (fisica & 0xFFFFF000) + 0x00000003;

  
  // Debemos llamar la función 'tlbflush' cada vez que modifiquemos
  // reliza la caché del procesador para las traducciones de
  // direcciones virtuales a físicas.
  tlbflush();
}

void mmu_unmapear_pagina(unsigned int virtual, unsigned int cr3) {
  unsigned int  numEntradaPD = virtual >> 22;
  unsigned int  numEntradaPT = (virtual >> 12) & 0x3FF;
  unsigned int* pPD = (unsigned int*) cr3;
  unsigned int* pPT = 0x0;

  //
  // Existe la entrada en el Directorio de Páginas?
  if (pPD[numEntradaPD] != 0x00000000) {

    pPT = (unsigned int*) (pPD[numEntradaPD] & 0xFFFFF000);
  
    
    
    //
    // Borramos la entrada.
    pPT[numEntradaPT] = 0x00000000;
    
    //
    // Debemos llamar la función 'tlbflush' cada vez que modifiquemos
    // realmente una estructura de paginación. La función 'tlbFlush'
    // actualiza la caché del procesador para las traducciones de
    // direcciones virtuales a físicas.
    tlbflush();
  }

  
}
\end{verbatim}

\subsection{Configuraci\'on del registro cr3}

Como se ve en el kernel.asm, llamamos a las funciones que definimos en mmu.h e implementamos en mmu.c y seteamos el registro cr3:

\begin{verbatim}
    ; Cargar directorio de paginas
     call inicializar_mmu


     call mmu_inicializar_dir_kernel
     
     mov eax, PD_DIR
     mov cr3, eax
\end{verbatim}
\subsection{Activaci\'on de paginaci\'on}

En este paso, simplemente activamos el flag correspondiente:

\begin{verbatim}
    ; Habilitar paginacion
      mov eax, cr0
      or  eax, 0x80000000
      mov cr0, eax	
\end{verbatim}

\subsection{Interrupciones}

La tabla de interrupciones est\'a definida en el archivo idt.c. Para ello, nos valemos de las estructuras que ofrece la c\'atedra. Usamos el macro IDT_ENTRY provisto por la c\'atedra. En la funci\'on idt_inicializa se inicializan los descriptores para cada uno de las interrupciones. En todos los casos, seteamos como selector de segmento el segmento 0x0048, que es el segmento que estamos usando para c\'odigo protegido. Tambi\'en se definen las rutinas que van a tratar las interrupciones:

\begin{verbatim}
#define IDT_ENTRY(numero)                                                                                        \
    idt[numero].offset_0_15 = (unsigned short) (
    (unsigned int)(&_isr ## numero) & (unsigned int) 0xFFFF);        \
    idt[numero].segsel = (unsigned short) 0x0048;                                                                  \
    idt[numero].attr = (unsigned short) 0x8E00;                                                                  \
    idt[numero].offset_16_31 = 
    (unsigned short) ((unsigned int)(&_isr ## numero) >> 16 & (unsigned int) 0xFFFF);

\end{verbatim}

 Inicialmente hab\'iamos configurado cada una de las interrupciones para que se pinte toda la pantalla en azul y se muestre el error. Para ello, utilizamos los macros utilizados por la p\'agina:

\begin{verbatim} 
 ;  call pintar_pantalla_azul
; imprimir_texto_mp MSG_INT%+%1, LONG_MSG_INT%+%1, 0x1F, 1, 1
\end{verbatim}

Esta parte qued\'o comentada. Posteriormente lo que hicimos fue para todas las interrupciones, capturar el error y desalojar la tarea, seteando el error:

\begin{verbatim}
_isr%1:
   cli
   pushfd 					; guarda del valor de los flags
   pushad

   call fin_intr_pic1 		; le comunica al pic que ya se atendio la interrupción

   push dword LONG_MSG_INT%+%1
   push dword MSG_INT%+%1
   call desalojar_tarea
   lea esp, [esp + 8]

   push dword -1
   call swap_tarea
   pop esi
   pushad
   call guardar_crs
   popad
   mov [selector], ax
   jmp FAR [offset]


   popad
   popfd
   sti
   iret
\end{verbatim}

Hay un manejo especial de tres interrupciones. La interrupci\'on por reloj, la interrupci\'on que maneja los servicios del kernel y finalmente la interrupci\'on de teclado.

\subsection{Interrupci\'on de Reloj}

Lo que hace b\'asicamente es avisar al pic que ya se atendi\'o la interrupci\'on 	y pasar a la pr\'oxima tarea usando la funci\'on sched_proximo_indice del scheduler.

\begin{verbatim}
_isr32:  ; Reloj
	cli
	pushfd 					; guarda del valor de los flags
	pushad
	call fin_intr_pic1 		; le comunica al pic que ya se atendio la interrupción
	
	call  sched_proximo_indice	; verificamos cual es el índice del gdt que tenemos que ejecutar
	
	str bx				; para evitar cambiar de tarea sin necesidad comparamos con el índice de tr

	cmp ax, bx
  	je .fin

	mov [selector], ax

  	jmp FAR [offset]

.fin:
	call proximo_reloj	
	popad
	popfd 				; restablece el valor de los flags
	sti	
	iret 				; retorna de la interrupción

\end{verbatim} 

\subsection{Interrupci\'on de Manejo de servicios de kernel}

El manejo de los servicios de kernel se hace por la interrupci\'on por software 0x52. Se encuentra en la funci\'on _isr82 del archivo isr.asm. Lo que hace es verificar el servicio solicitado y llamar a la funci\'on que la implementa en game.c (SYS_MINAR, SYS_MOVER y SYS_MISIL).

\subsection{Interrupci\'on de teclado}
El teclado se encuentra definido en _isr33 en el archivo isr.asm. Verifica si la tecla que obtuvo es p o es un n\'umero del teclado o del keypad (ya que se obtiene todo a trav\'es de la operaci\'on IN directamente de un puerto de I/O y las teclas del keypad tienen c\'odigos diferenciados). Por otra parte, se agreg\'o un archivo rand.c que genera n\'umeros pseudoaleatorios para pintar el color de forma distinto con cada tecla presionada y verificar qu\'e se presion\'o. Otra cosa que se hace es pintar la informaci\'on de la tarea en caso de que las teclas presionadas vayan del 1 al 8.

\subsection{Seteo de interrupciones desde el kernel}

Una vez configurada la tabla de interrupciones, se carga a trav\'es de la siguiente operaci\'on:
\begin{verbatim}
    ; Cargar IDT
      lidt [IDT_DESC]
\end{verbatim}

\subsection{Pantalla}

El manejo de lo que se imprime en la pantalla se encuentra dividido en dos partes. 

Por un lado, tenemos una serie de funciones definidas en el archivo screen.h e implementadas en el archivo screen.c. Por otro lado, para pintar el campo de $50 \times 50$ del mapa en modo real, tenemos funciones definidas en el archivo inicializar_pantalla.asm.

En la impresión del campo de $50 \times 50$ del mapa se usa la funci\'on inicializar_pantalla. Lo que hace es básicamente iterar por las 50 l\'ineas e imprimir en verde los 50 primeros caracteres de cada una de ellas. Para imprimirlos, se pasa directamente el valor 0x2A00, con la siguiente instrucci\'on:

\begin{verbatim}
      mov WORD [gs:edx],  0x2A00
\end{verbatim}

Recordemos qu\'e significa cada bit

\begin{verbatim}
Bit 76543210
    ||||||||
    |||||^^^-color del frente
    ||||^----para indicar si es un color fuerte
    |^^^-----color del fondo
    ^--------para indicar si el caracter pestañea
\end{verbatim}

De este modo, le enviamos el color 2A, que indica que el color de fondo es verde brillante y el color de frente verde oscuro. Y ya que queremos llenar en principio el campo vac\'io, le pasamos el caracter (byte) vac\'io 00.

\begin{codesnippet}
\begin{verbatim}
    ecx = 50
    ebx = 50
    edx = 0
    	gs = <segmento de video>
    Mientras ecx != 0
        ebx = 50
        ecx--
        Mientras ebx != 0
            Mover a (gs:edx) 0x2400
            edx += 2
            ebx--
        FinMientras
    FinMientras
\end{verbatim}
\end{codesnippet}

Las funciones de implementadas en screen.c se utilizan para imprimir o bien las tareas en el campo o bien la informaci\'on del contexto.

El punto de entrada para estas funciones se da en kernel.asm y es imprimir_contexto_vacio. Esta funci\'on simplemente imprime los relojes de cada tarea que se encuentra en la parte inferior de la pantalla.

\subsection{Tareas - Tanques}

En el archivo tss.c, definimos estructuras tss para cada uno de los tanques y tambi\'en para la tarea idle, y para la tarea inicial que se hace necesario para iniciar el intercambio de tareas.
Si bien tenemos una estructura definida para cada uno de los tanques, vamos a utilizar solamente dos entradas 3 entradas de la gdt, que son las siguientes:

\begin{verbatim}
//
  // TSS 1: segmento de estado para la Tarea Idle.
  //
  [GDT_IDX_TSS1] = (gdt_entry){ 
    (unsigned short) 0x0067, // Segment limit  0:15
    (unsigned short) 0x0000, // Base address   0:15
    (unsigned char)  0x00,   // Base address  15:23
    (unsigned char)  0x9,    // Type: siempre vale 0x9
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Descriptor Privilege Level (0: system)
    (unsigned char)  1,      // Segment Present (1: true; 0: false).
    (unsigned char)  0x0,    // Segment limit  16:19
    (unsigned char)  1,      // AVL (Available for use by system software)
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Granularity (0: byte units; 1: 4KB unit).
    (unsigned char)  0x00    // Base address  24:31
  },
  //
  // TSS 2: segmento de estado para realizar intercambio de tareas.
  //
  [GDT_IDX_TSS2] = (gdt_entry){ 
    (unsigned short) 0x0067, // Segment limit  0:15
    (unsigned short) 0x0000, // Base address   0:15
    (unsigned char)  0x00,   // Base address  15:23
    (unsigned char)  0x9,    // Type: siempre vale 0x9
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Descriptor Privilege Level (0: system)
    (unsigned char)  1,      // Segment Present (1: true; 0: false).
    (unsigned char)  0x0,    // Segment limit  16:19
    (unsigned char)  1,      // AVL (Available for use by system software)
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Granularity (0: byte units; 1: 4KB unit).
    (unsigned char)  0x00    // Base address  24:31
  },
  [GDT_IDX_INIT] = (gdt_entry){ 
    (unsigned short) 0x0067, // Segment limit  0:15
    (unsigned short) 0x0000, // Base address   0:15
    (unsigned char)  0x00,   // Base address  15:23
    (unsigned char)  0x9,    // Type: siempre vale 0x9
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Descriptor Privilege Level (0: system)
    (unsigned char)  1,      // Segment Present (1: true; 0: false).
    (unsigned char)  0x0,    // Segment limit  16:19
    (unsigned char)  1,      // AVL (Available for use by system software)
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Siempre vale 0.
    (unsigned char)  0,      // Granularity (0: byte units; 1: 4KB unit).
    (unsigned char)  0x00    // Base address  24:31
  }
\end{verbatim} 

La idea es que en el intercambio se vaya reemplazando la estructura de segmento de tarea apuntada por el descriptor, con las estructuras definidas para cada tanque y para la tarea idle. De esta manera, nos ahorramos definir un descriptor para cada una de las tareas y si bien tenemos un n\'umero de tareas fijas, nos permitir\'ia no depender de los l\'imites de entradas de la tabla gdt para la cantidad de procesos o tareas soportadas.

En tss.c tambi\'en se define la funci\'on que realiza el swapping:

\begin{verbatim}
// swapea tareas y devuelve el indice de la siguiente TSS a la que hay que saltar
unsigned short swap_tarea(unsigned int proxima_tarea) {
	unsigned short retVal = tss_activo==1?GDT_IDX_TSS2*8:GDT_IDX_TSS1*8;
	unsigned int dirTSS = (unsigned int) &tss_idle;
	if (proxima_tarea != -1) {
	   dirTSS = (unsigned int) &tss_tanques[proxima_tarea];
	}
	if (tss_activo == 1) {
	   retVal = GDT_IDX_TSS2*8; // debo devolver el índice al comienzo del selector
	   tss_activo = 2;
	   gdt[GDT_IDX_TSS2].base_0_15  = (unsigned short)  dirTSS;
	   gdt[GDT_IDX_TSS2].base_23_16 = (char) (dirTSS >> 16);
   	   gdt[GDT_IDX_TSS2].base_31_24 = (char) (dirTSS >> 24);
		
        } else {
	   retVal = GDT_IDX_TSS1*8;  // debo devolver el índice al comienzo del selector
	   tss_activo = 1;
	   gdt[GDT_IDX_TSS1].base_0_15  = (unsigned short)  dirTSS;
	   gdt[GDT_IDX_TSS1].base_23_16 = (char) (dirTSS >> 16);
   	   gdt[GDT_IDX_TSS1].base_31_24 = (char) (dirTSS >> 24);

	}
        return retVal;
}


\end{verbatim}
\subsection{Scheduler}

Para realizar el scheduler b\'asicamente tenemos la siguiente funci\'on:

sched_proximo_indice()

definida en el archivo sched.h que se encarga de definir cu\'al es el pr\'oximo tanque, representado por una tarea.
En el archivo game.c tenemos una variable global que nos indica cu\'al es la tarea activa en ese momento. Este archivo tambi\'en publica el m\'etodo proxima_tarea que nos indica cu\'al debe ser la pr\'oxima tarea siguiendo un schema roundrobin y sin considerar aquellas tareas que por alguna raz\'on fueron desalojadas.

\begin{verbatim}
unsigned int tarea_proxima() {
   if (juego_pausado)
	return IDLE;	// si el juego está pausado se devuelve -1 y la próxima tarea será IDLE
   int i = (id_tanque_activo + 1) % CANT_TANQUES;
   int cant_verificados = 0;
   while (cant_verificados < CANT_TANQUES) {
      if (tanques_activos[i] == 1) {
	id_tanque_activo = i;
	return i;
      }
      cant_verificados++;
      i = (i + 1) % CANT_TANQUES;
   }


   return IDLE; 	// en este caso se toma como que la tarea próxima tiene que ser IDLE
}
\end{verbatim}

Observamos que esta funci\'on en caso de que se encuentre el juego pausado (por presionar la tecla p), va a devolver IDLE. Esto nos sirve para indicar que la pr\'oxima tarea a ejecutar no corresponde a un tanque, sino que es la tarea idle. 

Si vemos la rutina que maneja la interrupci\'on por reloj, lo que hacemos es llamar a esta funci\'on y saltar a la nueva tarea con un far jmp.

\begin{verbatim}
_isr32:  ; Reloj
	cli
	pushfd 					; guarda del valor de los flags
	pushad
	call fin_intr_pic1 		; le comunica al pic que ya se atendio la interrupción
	
	call  sched_proximo_indice	; verificamos cual es el índice del gdt que tenemos que ejecutar
	
	str bx				; para evitar cambiar de tarea sin necesidad comparamos con el índice de tr

	cmp ax, bx
  	je .fin

	mov [selector], ax

  	jmp FAR [offset]

.fin:
	call proximo_reloj	
	popad
	popfd 				; restablece el valor de los flags
	sti	
	iret 				; retorna de la interrupción

\end{verbatim}
\end{document}

